/********************************************************************/
/* Copyright (c) 2011 DG Technologies Inc. and Yuzi Mizuno          */
/* All rights reserved.                                             */
/* Granted under the MIT license (see mg/MGCL.h for detail)         */
/********************************************************************/
#include "MGCLStdAfx.h"
#include "mg/Straight.h"
#include "mg/LBRep.h"
#include "mg/SurfCurve.h"
#include "mg/Tolerance.h"
#include "topo/PVertex.h"
#include "topo/Edge.h"
#include "topo/Face.h"

#if defined(_DEBUG)
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

//Test if this edge's start point(when start=true) and edge2 is connected
//and their directions are the same.
//When start=false, this edge's end point is tested.
bool MGEdge::is_connected_and_same_direction(
	bool start,
	const MGEdge& edge2
)const{
	const MGPVertex* pv1;
	const MGPVertex* pv2;
	if(start){
		pv1=vertex_start();
		pv2=edge2.vertex_end();
	}else{
		pv1=vertex_end();
		pv2=edge2.vertex_start();
	}
	return pv1->binder()==pv2->binder();
}

//Make a binder cell of this parameter cell.
//Returned is the binder pointer generated by new.
//The binder has no geometry, only has binder and parameter cell relationship.
MGCellNB* MGEdge::make_binder() const{
	MGCellNB* cell=binder();
	if(cell) return cell;
	MGEdge* bedge=new MGEdge();
	set_binder(*bedge);
	return bedge;
}

//Make this cell's binder cell's extent expression.
//Returned is a MGGeometry pointer generated by new.
//When this cell does not have star cell, null pointer will be returned.
//make_binder_extent() only makes the expression, and does nothing to
//the topology structure.
MGGeometry* MGEdge::make_binder_extent() const{
	const MGSurface* srf=face()->surface();
	return new MGSurfCurve(*srf,trimmed_curve());
}

//Make a binder associated with the world curve rep.
//Returned is the binder edge pointer.
//If the parameter edge already had the binder,
//make_binder_with_curve() only returns the pointer.
//*** This edge must be a parameter edge of a loop that is a boundary of a face.
MGEdge* MGEdge::make_binder_with_curve()const{
	assert(face());
	assert(face()->surface());

	const MGSurface* srf=face()->surface();
	MGEdge* bedg=binder_edge();
	if(bedg)
		if(bedg->base_curve()) return bedg;

	//If binder edge did not have curve representation.
	MGCurve* bcrv=new MGSurfCurve(*srf,trimmed_curve());

	double t0=param_s(), t1=param_e();
	/*MGPosition P0=eval_star(t0), P1=eval_star(t1);
	if(P0==P1){
		MGVector deriS=bcrv->eval(t0), deriE=bcrv->eval(t1);
		if((deriS.is_zero_vector() && deriE.is_zero_vector())
		|| (deriS.parallel(deriE) && deriS%deriE<0.)){
			delete bcrv;
			bcrv=new MGStraight(P1,P0);
		}
	}*/
	if(bedg) 
		bedg->set_extent(bcrv);
	else	//Generate binder edge.
		bedg=set_binder_edge(bcrv);

	(*bedg).set_start(t0);
	(*bedg).set_end(t1);
	return bedg;
}

//Make sure that this has an extent expression.
//When this did not have an extent, make the extent from the partner
//member's parameter expression and the star cell.
//This must be a binder cell that has partner members that are
//boundaries. When this is not the case or this had an extent already,
//it does nothing.
void MGEdge::make_extent() const{
	if(extent()) return;
	if(!is_bcell()) return;

	const MGEdge* pedge=member_partner_edge(0);
	assert(pedge->face()); assert(pedge->face()->surface());
	const MGSurface* srf=pedge->face()->surface();
	MGCurve* bcrv;
	//If binder edge did not have curve representation.
	bcrv=new MGSurfCurve(*srf,pedge->trimmed_curve());
	MGEdge* thisE=const_cast<MGEdge*>(this);
	thisE->set_extent(bcrv);
}

//Obtain the i-th member partner edge. This must be a binder edge.
const MGEdge* MGEdge::member_partner_edge(size_t i)const{
	return static_cast<const MGEdge*>(member_partner(i));
}

//Approximate the parameter edge by a polyline and replace this edge
//expression by the polyline. Polyline approximation is so done that
//the correspoinding binder edge can be appximated by the polyline connecting
//each binder edge's point that corresponds to the each this edge's point.
//(1) This must be a parameter cell edge.
//(2) This edge must be a member of a loop which is a boundary of a face.
//(3) If this edge did not have a binder edge, polygonize generates the binder edge.
//(The tolerance used to generate the binder is MGTolerance::line_zero(),
// not input error.)
//Input error is tolerance allowed between the polygon and the original curve.
void MGEdge::polygonize(double error){
	const MGSurface* srf = face()->surface();
	MGLBRep* Bcrv;
	MGEdge* bedge = binder_edge();
	double errorSave = MGTolerance::set_line_zero(error);
	if(bedge){
		if(!bedge->base_curve()){
			Bcrv = new MGLBRep(MGSurfCurve(*srf,trimmed_curve()), 2);
		}else{
			Bcrv = new MGLBRep(bedge->trimmed_curve(), 2);
		}
		bedge->set_extent(Bcrv);
	}else{
		Bcrv = new MGLBRep(MGSurfCurve(*srf,trimmed_curve()), 2);
		set_binder_edge(Bcrv);
		bedge = binder_edge();
	}
	MGTolerance::set_line_zero(errorSave);
	
	MGLBRep* nBcrv = dynamic_cast<MGLBRep*>(bedge->curve_limitted());
	double t0,t1;
	if(equal_direction_to_binder()){ t0 = param_s(); t1 = param_e();}
	else{ t1 = param_s(); t0 = param_e();}
	nBcrv->change_range(t0, t1);
		//Now nBcrv is the same direction as this parameter edge, and has
		//the same parameter range.

	//Change Bcrv to original parameter edge's polyline expression.
	MGCurve* originalC=base_curve();
	size_t n = nBcrv->bdim();
	MGBPointSeq coef(n,2);
	const MGKnotVector& t = nBcrv->knot_vector();
	for(size_t i = 0; i < n; i++) coef.store_at(i, originalC->eval(t[i+1]));
	nBcrv->line_bcoef() = coef;
	set_extent(nBcrv);
}
